#!/usr/bin/python
#-*-coding: utf-8 -*-

"""This is the Deemon. It integrates the Cluster's interface, 
the connection and the communication with the Database.

"""

import os 
import sys
import time
import types
import zipfile

from clusters import setOS
from data import Project, Job
from connectSQL import ConnectSQL
from config import configs

from exceptions_ import Erro, ErroSQL, ErroJob, ErroCompilacao

__version__ = '3.3.1'

class Deemon(object):
    """The Deemon Class."""
     
    def work(self):
      """The main eternum loops.
      
      In this method, the Deemon connect with the DB,
      capture jobs, send jobs to cluster, get the results
      and send them to DB.
      
      Until the v3.3, this loop is infinite and only end if it crashes.
      In future version, a way to close de program without a 
      KeyboardInterrupt Exception will be implemented.
      
      In future, this method will be subdivided to stay more clean. 
      
      """
      
      # Test if the OS is supported and set the Cluster.
      cluster = setOS(configs)
      if (cluster is None): 
        # there is a error at create the cluster object.
        return
      else:
        print "Running Interop Router Deemon %s over %s" %(__version__ , cluster.__name__)
        
      # Connect to the Web Server
      connection = ConnectSQL(configs)
      if (not connection):
         return None
      
      idle = 0
      
      while(True): # Esse Loop vira thread.
         
         # waiting for a new job
         print 'Waiting for a job.'
            
         # send the system free resources to database.
         idleAux = idle
         idle = cluster.idleness()
         
         # avoiding write database all the time
         if idleAux != idle:
            connection.sendWorkLoad(cluster.id, idle)
            
         while(True):
            
            time.sleep(1.0)         # avoiding read database all the time
                                    # this is necessary only in tests cases, 
                                    # when the time to processe the job is short.
            jobFields = connection.getJob(cluster.id)
            
            if(jobFields):
               break
         
         ######################
         # THREADS HEREEEE!. =P
         ######################
         
         print 'New job found: ', jobFields[0]
         
         # creates a new Job
         job = Job(jobFields)
         
         # get the Project's parameters      
         projectFields = connection.getProject(job.projectId)
         
         # creates a new Project
         projeto = Project(job.projectId, projectFields)
         
         # Creates the Project Directory
         projDir = os.path.join(cluster.shareFolder, 'project%d' % (job.projectId))
         try:
            os.mkdir(projDir)
         except:
            # por enquanto vamos assumir q qq erro eh pq o diretorio jah existe.
            # ver os codigos de erro para isso, mas jah vi que depende do SO.
            
            # por enquanto se tem pasta tem executavel, 
            # com thread precisa testar se tem o exe e esperar compilacao
            # if (not existe exe) wait, ou algo assim
            pass
         else:
            # without directoy => without source!
            projeto.sourceCode = connection.getSourceCode(projeto.id)
            
            # Creates the .zip
            zipName = os.path.join(projDir, 'source.zip')
            
            outzip = open(zipName, 'wb')
            outzip.write(projeto.sourceCode)
            outzip.close()
            
            # Read the zip file
            zip = zipfile.ZipFile(zipName, 'r')
            
            # Extract each file in zip
            for name in zip.namelist():
               outfile = open(os.path.join(projDir, name), 'wb')
               outfile.write(zip.read(name))
               outfile.close()
                    
            # Compiles the source
            try:
               cluster.compileProject(projeto.id)
            except ErroCompilacao:
               print 'Compilation error, Project', projeto.id
            else:
               print 'Source of Project', projeto.id, 'has compiled.'
                  
         # Creates the Job Directory
         jobDir = os.path.join(projDir, 'job%d' % (job.id))
         try:
            os.mkdir(jobDir)
         except:
            # Job is already running in another thread.
            # Abort.
            # Aqui tambem precisa ser mais especifico quanto ao erro.
            print 'Job is already runnig in another place.'
            # volta a esperar por um job.
         else:
            # Save the .dat file
            datFile = open(projDir + os.sep + job.name, 'wb')
            datFile.write(job.dataFile)
            datFile.close()
   
            # submit the job to cluster
            try:
               jobClusterId = cluster.submitJob(projeto, job)
            except ErroJob, erro:
         #   #   # no Linux, o erro aparece aqui
               if erro.TipoErro == 'UID':
                  print 'Bad UID, run the Deemon with a valid UID for Job Submition. Cannot be the root to run a Job'
               else:
                  if erro.TipoErro == 'FAIL':
                     print 'Job %d Failed.' % (job.id)
                  else:
                     print 'Submition Error, Job', job.id    
         #   #   #   # se n for UID chama
                  try:
                     connection.sendResults(cluster, job, 3)
                  except ErroSQL:
                     print 'Cannot send error log from', job.id
                  else:
                     print 'Error log sent, job', job.id
                     
            else:
               print 'Job', job.id, 'submetido.'

         #   #   # Verifica se o Job terminou.
               print 'Waiting the job %d to finish.' % (job.id)
               try:
                  # here would be better a sleep 
                  while(not cluster.jobIsComplete(jobClusterId)): pass
               except ErroJob, erro:
         #   #   #   # no windows, o erro aparece aqui
                  if erro.TipoErro == 'CANCEL':
                     print 'Job %d of Project %d Cancelled.' % (job.id, projeto.id)
                  else:
                     if erro.TipoErro == 'FAIL':
                        print 'Job %d of Project %d Failed.' % (job.id, projeto.id)
                     else:
                        print 'Error, Job', job.id
         #   #   #   #   # se n for CANCEL chama
                     try:
                        connection.sendResults(cluster, job, 3)
                     except ErroSQL:
                        print 'Cannot send error log from job', job.id
                     else:
                        print 'Error log sent, job', job.id
               else:
         #   #   #   # deu certo!
                  print 'Job %d of Project %d finished.' % (job.id, projeto.id)
                  try:
                     connection.sendResults(cluster, job, 2)
                  except ErroSQL:
                     print 'Cannot send result from job', job.id
                  else:
                     print 'Result sent, job', job.id, '\n'

      return #   
      
      
if __name__ == "__main__": 
   deemon = Deemon()
   deemon.work()
   
